package org.hackystat.devcathlon.engine.event.impl;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import javax.xml.datatype.XMLGregorianCalendar;
import org.hackystat.devcathlon.configuration.ConfigurationManager;
import org.hackystat.devcathlon.engine.event.Event;
import org.hackystat.devcathlon.engine.event.EventScore;
import org.hackystat.devcathlon.engine.event.EventScoreManager;
import org.hackystat.devcathlon.engine.match.Match;
import org.hackystat.devcathlon.engine.team.Team;
import org.hackystat.devcathlon.engine.test.DevcathlonTestHelper;
import org.hackystat.sensorbase.resource.projects.jaxb.Project;
import org.hackystat.utilities.tstamp.Tstamp;
import org.junit.BeforeClass;
import org.junit.Test;


/**
 * Tests the class DontWaitUntilLastMin Event.
 * 
 * @author John Ly, John Zhou
 * 
 */
public class TestDontWaitUntilLastMin extends DevcathlonTestHelper {

  /** Count for the last days period. */
  private static int lastDaysCount = 3;
  /** Number of conditions that fail to meet requirements. */
  private static int conditionsCount = 2;
  /** Percentage upper threshold for conditions not met. */
  private static int percentThreshhold = 50;

  private String teamName = "TestTeam";
  private String teamOwner = "TeamOwner";
  private String matchName = "TestMatch";
  private String member1 = "Member1";
  private String member2 = "Member2";

  /**
   * Sets up the DevcathlonConfiguration for use in these test cases.
   * 
   * @throws Exception If problems occur setting up the configuration.
   */
  @BeforeClass
  public static void setupConfiguration() throws Exception {
    // Set up the testing DevcathlonConfiguration for the event BestCoverage.
    String eventName = DontWaitUntilLastMin.eventName;
    ConfigurationManager configManager = ConfigurationManager.getInstance();
    configManager.setBaseScoreWeight(eventName, 5);
    configManager.getConfiguration(eventName);
    configManager.setWakeupInterval(eventName, 1440);
    configManager.addProperty(eventName, DontWaitUntilLastMin.lastDaysKey, String
        .valueOf(lastDaysCount));
    configManager.addProperty(eventName, DontWaitUntilLastMin.conditionsKey, String
        .valueOf(conditionsCount));
    configManager.addProperty(eventName, DontWaitUntilLastMin.percentThreshholdKey, String
        .valueOf(percentThreshhold));
  }

  /**
   * Tests this event when there is no data.
   * 
   * @throws Exception If error retrieving data.
   */
  @Test
  public void testNoData() throws Exception {
    // Create the team
    Team team = makeTeam(teamName, teamOwner, member1, member2);

    // Now create the event instance.
    Event event = new DontWaitUntilLastMin();

    // Create a simple match containing a single team, a single project, and this event.
    Match match = addSimpleMatch(matchName, team, getProject(teamOwner, teamName), event);

    // average data is 0 since no data is created prior to penalty period

    XMLGregorianCalendar timestamp = Tstamp.makeTimestamp();
    EventScore score = event.computeScore(match, team, timestamp);
    EventScoreManager.getInstance().updateEventScore(score);
    assertEquals("Check base score 0", 0, score.getBaseScore());
    assertTrue("Check explanation 0", score.getExplanation().contains("not within penalty period"));
    assertEquals("Check points 0", 0, event.computePoints(score));

    score = event.computeScore(match, team, match.getEndTime());
    EventScoreManager.getInstance().updateEventScore(score);
    assertEquals("Check base score 1", 0, score.getBaseScore());
    assertTrue("Check explanation 1", score.getExplanation().contains("did not exceed threshhold"));
    assertEquals("Check points 1", 0, event.computePoints(score));

    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }

  /**
   * Tests this event when there is no penalty.
   * 
   * @throws Exception If error retrieving data.
   */
  @Test
  public void testNoPenalty() throws Exception {
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    Event event = new DontWaitUntilLastMin();
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);

    // create build, commit, devtime before penalty period
    makeBuildData(team.getOwner(), project, Tstamp.makeTimestamp());
    makeCommitData(team.getOwner(), project, Tstamp.makeTimestamp());
    makeDevTimeData(team.getOwner(), project, Tstamp.makeTimestamp(), 10);

    // test score before penalty period
    XMLGregorianCalendar timestamp = Tstamp.makeTimestamp();
    EventScore score = event.computeScore(match, team, timestamp);
    EventScoreManager.getInstance().updateEventScore(score);
    assertEquals("Check base score 2", 0, score.getBaseScore());
    assertTrue("Check explanation 2", score.getExplanation().contains("not within penalty period"));
    assertEquals("Check points 2", 0, event.computePoints(score));

    // create build, commit, devtime data 2 days before end of match
    XMLGregorianCalendar withinPenaltyDate = Tstamp.incrementDays(match.getEndTime(), -2);
    makeBuildData(team.getOwner(), project, withinPenaltyDate);
    withinPenaltyDate = Tstamp.incrementMilliseconds(withinPenaltyDate, 50);
    makeCommitData(team.getOwner(), project, withinPenaltyDate);
    withinPenaltyDate = Tstamp.incrementMilliseconds(withinPenaltyDate, 50);
    makeDevTimeData(team.getOwner(), project, withinPenaltyDate, 10);

    // test score 1 day before end of match
    withinPenaltyDate = Tstamp.incrementDays(match.getEndTime(), 1);
    score = event.computeScore(match, team, withinPenaltyDate);
    EventScoreManager.getInstance().updateEventScore(score);
    
    assertEquals("Check base score 3", 0, score.getBaseScore());
    assertTrue("Check explanation 3", score.getExplanation().contains("did not exceed threshhold"));
    assertEquals("Check points 3", 0, event.computePoints(score));

    // create build, commit, devtime data 1 day before end of match
    withinPenaltyDate = Tstamp.incrementMilliseconds(withinPenaltyDate, 50);
    makeBuildData(team.getOwner(), project, withinPenaltyDate);
    withinPenaltyDate = Tstamp.incrementMilliseconds(withinPenaltyDate, 50);
    makeCommitData(team.getOwner(), project, withinPenaltyDate);
    withinPenaltyDate = Tstamp.incrementMilliseconds(withinPenaltyDate, 50);
    makeDevTimeData(team.getOwner(), project, withinPenaltyDate, 10);

    // test score for last day
    score = event.computeScore(match, team, match.getEndTime());
    EventScoreManager.getInstance().updateEventScore(score);
    assertEquals("Check base score 4", 0, score.getBaseScore());
    assertTrue("Check explanation 4", score.getExplanation().contains("did not exceed threshhold"));
    assertEquals("Check points 4", 0, event.computePoints(score));

    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }

  /**
   * Tests penalty for this event with no data before penalty period.
   * 
   * @throws Exception If error retrieving data.
   */
  @Test
  public void testPenaltyWithNoPriorData() throws Exception {
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    Event event = new DontWaitUntilLastMin();
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);

    // create data within penalty period that exceeds the average
    // the events timestamp needs to be different for each to avoid errors
    XMLGregorianCalendar withinPenaltyDate = Tstamp.incrementDays(match.getEndTime(), -1);
    makeBuildData(team.getOwner(), project, withinPenaltyDate);
    withinPenaltyDate = Tstamp.incrementMilliseconds(withinPenaltyDate, 50);
    makeCommitData(team.getOwner(), project, withinPenaltyDate);
    withinPenaltyDate = Tstamp.incrementMilliseconds(withinPenaltyDate, 50);
    makeDevTimeData(team.getOwner(), project, withinPenaltyDate, 30);

    // test score at end of match
    EventScore score = event.computeScore(match, team, match.getEndTime());
    EventScoreManager.getInstance().updateEventScore(score);
    assertEquals("Check base score 5", -1, score.getBaseScore());
    assertTrue("Check explanation 5", score.getExplanation().contains("working over"));
    assertEquals("Check points 5", -5, event.computePoints(score));

    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }

  /**
   * Tests this event when there is data prior to penalty period.
   * 
   * @throws Exception If error retrieving the data.
   */
  @Test
  public void testPenaltyWithPriorData() throws Exception {
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    Event event = new DontWaitUntilLastMin();
    Project project = getProject(teamOwner, teamName);
    // Create a simple match containing a single team, a single project, and this event.
    // match length = 5 days
    Match match = addSimpleShortMatch(matchName, team, project, event);

    // create enough data so
    // averageBuilds = 2, averageCommits = 2, averageDevTime = 10
    for (int i = 0; i < 4; i++) {
      makeBuildData(team.getOwner(), project, Tstamp.makeTimestamp());
      makeCommitData(team.getOwner(), project, Tstamp.makeTimestamp());
      makeDevTimeData(team.getOwner(), project, Tstamp.makeTimestamp(), 5);
    }
    
    //create build, commit, devtime data
    XMLGregorianCalendar withinPenaltyDate = Tstamp.incrementDays(match.getEndTime(), -2);
    for (int i = 0; i < 5; i++) {
      makeBuildData(team.getOwner(), project, withinPenaltyDate);
      withinPenaltyDate = Tstamp.incrementMilliseconds(withinPenaltyDate, 50);
      makeCommitData(team.getOwner(), project, withinPenaltyDate);
      withinPenaltyDate = Tstamp.incrementMilliseconds(withinPenaltyDate, 50);
      makeDevTimeData(team.getOwner(), project, withinPenaltyDate, 5);
      withinPenaltyDate = Tstamp.incrementMilliseconds(withinPenaltyDate, 50);
    }
    
    // compute the score
    // should trigger penalty since average devtime and average build exceeds 50% threshold
    EventScore score = event.computeScore(match, team, withinPenaltyDate);
    EventScoreManager.getInstance().updateEventScore(score);
    assertEquals("Check base score 6", -1, score.getBaseScore());
    assertTrue("Check explanation 6", score.getExplanation().contains("working over"));
    assertEquals("Check points 6", -5, event.computePoints(score));
    
    //create build, commit, devtime data for second day
    withinPenaltyDate = Tstamp.incrementDays(withinPenaltyDate, 1);
    for (int i = 0; i < 5; i++) {
      makeBuildData(team.getOwner(), project, withinPenaltyDate);
      withinPenaltyDate = Tstamp.incrementMilliseconds(withinPenaltyDate, 50);
      makeCommitData(team.getOwner(), project, withinPenaltyDate);
      withinPenaltyDate = Tstamp.incrementMilliseconds(withinPenaltyDate, 50);
      makeDevTimeData(team.getOwner(), project, withinPenaltyDate, 5);
      withinPenaltyDate = Tstamp.incrementMilliseconds(withinPenaltyDate, 50);
    }
    
    //test score for last day
    score = event.computeScore(match, team, match.getEndTime());
    assertEquals("Check base score 7", -1, score.getBaseScore());
    assertTrue("Check explanation 7", score.getExplanation().contains("working over"));
    assertEquals("Check points 7", -5, event.computePoints(score));

    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
}
